/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package br.com.foxline.service;

import br.com.foxline.atlasmobile.enums.*;
import br.com.foxline.model.DadosIptu;
import br.com.foxline.model.Imovel;
import br.com.foxline.model.Iptu;
import br.com.foxline.model.Parcela;
import com.lowagie.text.Document;
import com.lowagie.text.DocumentException;
import com.lowagie.text.pdf.PdfContentByte;
import com.lowagie.text.pdf.PdfImportedPage;
import com.lowagie.text.pdf.PdfReader;
import com.lowagie.text.pdf.PdfWriter;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.RoundingMode;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.ejb.Stateless;
import javax.persistence.Query;
import org.jrimum.bopepo.BancosSuportados;
import org.jrimum.bopepo.Boleto;
import org.jrimum.bopepo.exemplo.Exemplos;
import org.jrimum.bopepo.pdf.Files;
import org.jrimum.bopepo.view.BoletoViewer;
import org.jrimum.domkee.financeiro.banco.Agencia;
import org.jrimum.domkee.financeiro.banco.febraban.Carteira;
import org.jrimum.domkee.financeiro.banco.febraban.ContaBancaria;
import org.jrimum.domkee.financeiro.banco.febraban.NumeroDaConta;
import org.jrimum.domkee.financeiro.banco.febraban.Titulo;
import org.jrimum.utilix.ClassLoaders;

/**
 *
 * @author luciano
 */
@Stateless
public class IptuService extends GenericService<Iptu> {

    public IptuService() {
        super(Iptu.class);
    }

    public List<Parcela> gerarParcelas(Imovel imovel, BigDecimal valor, int qtdParcelas, Date date) {
        List<Parcela> parcelas = new ArrayList<Parcela>(qtdParcelas);

        Calendar calendar = GregorianCalendar.getInstance();
        calendar.setTime(date);
        BigDecimal valorParcelado = valor.divide(new BigDecimal(qtdParcelas), RoundingMode.UP);

        for (int i = 0; i < qtdParcelas; i++) {
            Parcela parcela = new Parcela();
            parcela.setIndice(i + 1);
            calendar.add(Calendar.MONTH, i);
            parcela.setDataParcela(calendar.getTime());
            parcela.setValor(valorParcelado);
            parcelas.add(parcela);
        }

        return parcelas;
    }

    public BigDecimal iptu(Imovel imovel, DadosIptu dadosIptu) {
        System.out.println("VVT : " + valorVenalDoTerreno(imovel));
        System.out.println("VVE : " + valorVenalDaEdificacao(imovel));
        System.out.println("VVI : " + valorVenalDoImovel(imovel));

        BigDecimal valor = valorVenalDoImovel(imovel);
        valor.divide(new BigDecimal(100)).multiply(aliquota(dadosIptu, valor, imovel.getUtilizacao().equals(Utilizacao.RESIDENCIAL), !imovel.getOcupacao().equals(Ocupacao.NAO_CONSTRUIDO)));
        return valor;
    }

    public BigDecimal iptuAvista(Imovel imovel, DadosIptu dadosIptu) {
        return iptu(imovel, dadosIptu).multiply(new BigDecimal(0.9));
    }

    public BigDecimal valorVenalDoImovel(Imovel imovel) {
        return valorVenalDoTerreno(imovel).add(valorVenalDaEdificacao(imovel));
    }

    private BigDecimal valorVenalDoTerreno(Imovel imovel) {
        return AT(imovel).multiply(fatorDeMultiplicacao(imovel)).multiply(fatorDeLocalizacao(imovel)).multiply(topografia(imovel)).multiply(situacao(imovel)).multiply(pedologia(imovel));
    }

    private BigDecimal valorVenalDaEdificacao(Imovel imovel) {
        System.out.println(areaConstruidaDaUnidade(imovel));
        System.out.println(fatorDeMultiplicacaoDaUnidade(imovel));
        System.out.println(caracteristicaDoImovel(imovel));
        System.out.println(conservacao(imovel));
        System.out.println(subtipo(imovel));
        return areaConstruidaDaUnidade(imovel).multiply(fatorDeMultiplicacaoDaUnidade(imovel)).multiply(caracteristicaDoImovel(imovel).divide(new BigDecimal(100))).multiply(conservacao(imovel)).multiply(subtipo(imovel));
    }

    /**
     * Get todos os iptus referente ao imóvel.
     *
     * @param imovel
     * @return Lista de iptus do imovel.
     */
    public List<Iptu> getIptus(Imovel imovel) {
        String sql = "SELECT i FROM Iptu i WHERE i.imovel.id = :id order by i.dataIptu desc";
        Query query = getEntityManager().createQuery(sql);
        query.setParameter("id", imovel.getId());
        return (List<Iptu>) query.getResultList();
    }

    /**
     * Verificar o valor do VRM - fórmula --> TLP = (TI x 2 %) x VRM
     *
     * @param imovel
     * @return
     */
    public BigDecimal tlp(Imovel imovel, BigDecimal ufmp) {
        return imovel.getDimensoes().getTestadaImovel().multiply(new BigDecimal(0.02)).multiply(ufmp.multiply(new BigDecimal(6)));
    }

    /**
     * Verificar o valor do VRM - fórmula --> TCL = (AI x 1 %) x VRM
     *
     * @param imovel
     * @return
     */
    public BigDecimal tcl(Imovel imovel, BigDecimal ufmp) {
        return imovel.getDimensoes().getAreaTerreno().multiply(new BigDecimal(0.01)).multiply(ufmp.multiply(new BigDecimal(6)));
    }

    /**
     * verificar fórmula
     *
     * @param imovel
     * @return
     */
    public BigDecimal tcc(Imovel imovel, BigDecimal ufmp) {
        return imovel.getDimensoes().getTestadaImovel().multiply(new BigDecimal(0.02)).multiply(ufmp.multiply(new BigDecimal(6)));
    }

    public BigDecimal aliquota(DadosIptu dadosIptu, BigDecimal vvi, boolean residencial, boolean naoEdificado) {
        if (residencial && ((vvi.compareTo(dadosIptu.value(800)) == 1)
                && (vvi.compareTo(dadosIptu.value(8700)) != 1))) {
            return new BigDecimal(1);
        } else if (residencial && (vvi.compareTo(dadosIptu.value(8700)) == 1)) {
            return new BigDecimal(1.5);
        } else if (!residencial && (vvi.compareTo(dadosIptu.value(2900)) != 1)) {
            return new BigDecimal(1);
        } else if (!residencial && (vvi.compareTo(dadosIptu.value(2900)) == 1)) {
            return new BigDecimal(1.5);
        } else if (naoEdificado) {
            return new BigDecimal(1.5);
        }
        return new BigDecimal(1);
    }

    /**
     * TODO - verificar o calculo Area do Terreno
     */
    private BigDecimal AT(Imovel imovel) {
        int qtd = imovel.getInscricaoCadastralUnid();
        if (qtd == 1) {
            return imovel.getDimensoes().getAreaTerreno().divide(new BigDecimal(100));
        } else {
            return (imovel.getDimensoes().getAreaTerreno().multiply(imovel.getDimensoes().getAreaUnidade()).divide(imovel.getDimensoes().getAreaTotalEdificacao())).divide(new BigDecimal(100));
        }
    }

    private BigDecimal fatorDeMultiplicacao(Imovel imovel) {
        if (imovel.getOcupacao().equals(Ocupacao.NAO_CONSTRUIDO)) {
            return new BigDecimal(20000);
        } else {
            return new BigDecimal(28000);
        }
    }

    private BigDecimal fatorDeLocalizacao(Imovel imovel) {
        return new BigDecimal(imovel.getFatorLocalizacao()).divide(new BigDecimal(100));
    }

    private BigDecimal topografia(Imovel imovel) {
        if (imovel.getTopografia().equals(Topografia.PLANO)) {
            return new BigDecimal(1);
        } else if (imovel.getTopografia().equals(Topografia.ACLIVE)) {
            return new BigDecimal(0.9);
        } else if (imovel.getTopografia().equals(Topografia.DECLIVE)) {
            return new BigDecimal(0.8);
        }
        return new BigDecimal(0);
    }

    private BigDecimal situacao(Imovel imovel) {
        if (imovel.getSituacaoTerreno().equals(SituacaoTerreno.ESQUINA_OU_DUAS_FRENTES)) {
            return new BigDecimal(1.1);
        } else if (imovel.getSituacaoTerreno().equals(SituacaoTerreno.UMA_FRENTE)) {
            return new BigDecimal(1.0);
        } else if (imovel.getSituacaoTerreno().equals(SituacaoTerreno.ENCRAVADO_OU_VILA)) {
            return new BigDecimal(0.8);
        }
        return new BigDecimal(0);
    }

    private BigDecimal pedologia(Imovel imovel) {
        if (imovel.getPedologia().equals(Pedologia.ALAGADO)) {
            return new BigDecimal(0.6);
        } else if (imovel.getPedologia().equals(Pedologia.INUNDAVEL)) {
            return new BigDecimal(0.7);
        } else if (imovel.getPedologia().equals(Pedologia.ROCHOSO)) {
            return new BigDecimal(0.8);
        } else if (imovel.getPedologia().equals(Pedologia.NORMAL)) {
            return new BigDecimal(1);
        } else if (imovel.getPedologia().equals(Pedologia.ARENOSO)) {
            return new BigDecimal(1.9);
        }
        return new BigDecimal(0);
    }

    private BigDecimal areaConstruidaDaUnidade(Imovel imovel) {
        return imovel.getDimensoes().getAreaUnidade();
    }

    private BigDecimal fatorDeMultiplicacaoDaUnidade(Imovel imovel) {
        return new BigDecimal(8000);
    }

    private BigDecimal caracteristicaDoImovel(Imovel imovel) {
        BigDecimal result = new BigDecimal(0);

        //Revestimento Interno
        if (imovel.getRevExterno().equals(RevExterno.SEM_REVESTIMENTO)) {
            result = result.add(new BigDecimal(0));
        } else if (imovel.getRevExterno().equals(RevExterno.OLEO)) {
            result = result.add(new BigDecimal(23));
        } else if (imovel.getRevExterno().equals(RevExterno.CAIACAO)) {
            result = result.add(new BigDecimal(17));
        } else if (imovel.getRevExterno().equals(RevExterno.MADEIRA)) {
            result = result.add(new BigDecimal(12));
        } else if (imovel.getRevExterno().equals(RevExterno.OUTROS)) {
            result = result.add(new BigDecimal(20));
        }

        //Pisos
        if (imovel.getPiso().equals(Piso.TERRA_BATIDA)) {
            result = result.add(new BigDecimal(0));
        } else if (imovel.getPiso().equals(Piso.CIMENTO)) {
            result = result.add(new BigDecimal(10));
        } else if (imovel.getPiso().equals(Piso.CERAMICA_OU_MOSAICO)) {
            result = result.add(new BigDecimal(17));
        } else if (imovel.getPiso().equals(Piso.OUTROS)) {
            result = result.add(new BigDecimal(20));
        }

        //Forros
        if (imovel.getForro().equals(Forro.INEXISTENTE)) {
            result = result.add(new BigDecimal(0));
        } else if (imovel.getForro().equals(Forro.MADEIRA)) {
            result = result.add(new BigDecimal(3));
        } else if (imovel.getForro().equals(Forro.ESTUQUE)) {
            result = result.add(new BigDecimal(3));
        } else if (imovel.getForro().equals(Forro.LAJE)) {
            result = result.add(new BigDecimal(4));
        }

        //Cobertura
        if (imovel.getCobertura().equals(Cobertura.PALHA_ZINCO_CAVACO)) {
            result = result.add(new BigDecimal(3));
        } else if (imovel.getCobertura().equals(Cobertura.FIBRO_CIMENTO)) {
            result = result.add(new BigDecimal(6));
        } else if (imovel.getCobertura().equals(Cobertura.TELHA_BARRO)) {
            result = result.add(new BigDecimal(8));
        } else if (imovel.getCobertura().equals(Cobertura.LAJE)) {
            result = result.add(new BigDecimal(10));
        }

        //Instalação Sanitária
        if (imovel.getInstSanitaria().equals(InstSanitaria.INEXISTENTE)) {
            result = result.add(new BigDecimal(0));
        } else if (imovel.getInstSanitaria().equals(InstSanitaria.EXTERNA)) {
            result = result.add(new BigDecimal(1));
        } else if (imovel.getInstSanitaria().equals(InstSanitaria.INTERNA)) {
            result = result.add(new BigDecimal(2));
        } else if (imovel.getInstSanitaria().equals(InstSanitaria.MAIS_DE_UMA_INTERNA)) {
            result = result.add(new BigDecimal(3));
        }

        //Instalação Elétrica
        if (imovel.getInstalacoesEletricas().equals(InstalacoesEletricas.INEXISTENTE)) {
            result = result.add(new BigDecimal(0));
        } else if (imovel.getInstalacoesEletricas().equals(InstalacoesEletricas.APARENTE)) {
            result = result.add(new BigDecimal(8));
        } else if (imovel.getInstalacoesEletricas().equals(InstalacoesEletricas.EMBUTIDA)) {
            result = result.add(new BigDecimal(12));
        }

        //Estrutura
        if (imovel.getEstrutura().equals(Estrutura.CONCRETO)) {
            result = result.add(new BigDecimal(28));
        } else if (imovel.getEstrutura().equals(Estrutura.ALVENARIA)) {
            result = result.add(new BigDecimal(18));
        } else if (imovel.getEstrutura().equals(Estrutura.MADEIRA)) {
            result = result.add(new BigDecimal(11));
        } else if (imovel.getEstrutura().equals(Estrutura.METALICA)) {
            result = result.add(new BigDecimal(26));
        }

        return result;
    }

    private BigDecimal conservacao(Imovel imovel) {
        if (imovel.getEstadoConservacao().equals(EstadoConservacao.BOM)) {
            return new BigDecimal(1);
        } else if (imovel.getEstadoConservacao().equals(EstadoConservacao.REGULAR)) {
            return new BigDecimal(0.8);
        } else if (imovel.getEstadoConservacao().equals(EstadoConservacao.MAU)) {
            return new BigDecimal(0.5);
        }
        return new BigDecimal(0);
    }

    private BigDecimal subtipo(Imovel imovel) {
        if (imovel.getPosicao().equals(Posicao.ISOLADA) && imovel.getFachada().equals(Fachada.ALINHADA)) {
            return new BigDecimal(0.9);
        } else if (imovel.getPosicao().equals(Posicao.ISOLADA) && imovel.getFachada().equals(Fachada.RECUADA)) {
            return new BigDecimal(1);
        } else if (imovel.getPosicao().equals(Posicao.GEMINADA) && imovel.getFachada().equals(Fachada.ALINHADA)) {
            return new BigDecimal(0.7);
        } else if (imovel.getPosicao().equals(Posicao.GEMINADA) && imovel.getFachada().equals(Fachada.RECUADA)) {
            return new BigDecimal(0.8);
        } else if (imovel.getPosicao().equals(Posicao.SUPERPOSTA) && imovel.getFachada().equals(Fachada.ALINHADA)) {
            return new BigDecimal(0.8);
        } else if (imovel.getPosicao().equals(Posicao.SUPERPOSTA) && imovel.getFachada().equals(Fachada.RECUADA)) {
            return new BigDecimal(0.9);
        } else if (imovel.getPosicao().equals(Posicao.CONJUGADA) && imovel.getFachada().equals(Fachada.ALINHADA)) {
            return new BigDecimal(0.8);
        } else if (imovel.getPosicao().equals(Posicao.CONJUGADA) && imovel.getFachada().equals(Fachada.RECUADA)) {
            return new BigDecimal(0.9);
        }
        return new BigDecimal(0);
    }

    public int qtdParcelas(BigDecimal valor) {
        if (valor.compareTo(new BigDecimal(500)) != 1) {
            return 0;
        } else if (valor.compareTo(new BigDecimal(200)) != 1) {
            return 4;
        } else if (valor.compareTo(new BigDecimal(600)) != 1) {
            return 6;
        } else if (valor.compareTo(new BigDecimal(1200)) != 1) {
            return 10;
        } else if (valor.compareTo(new BigDecimal(2400)) != 1) {
            return 12;
        } else if (valor.compareTo(new BigDecimal(6000)) != 1) {
            return 18;
        } else if (valor.compareTo(new BigDecimal(10000)) != 1) {
            return 20;
        } else if (valor.compareTo(new BigDecimal(15000)) != 1) {
            return 24;
        } else if (valor.compareTo(new BigDecimal(30000)) != 1) {
            return 28;
        } else if (valor.compareTo(new BigDecimal(50000)) != 1) {
            return 32;
        } else if (valor.compareTo(new BigDecimal(50000)) == 1) {
            return 36;
        }
        return 0;
    }

    
    
    //########### BOLETO #######################
    public void gerarBoleto() {

        Boleto boletoBBNossoNumero10 = crieBoletoBBNossoNumero10();
        Boleto boletoBradesco = crieBoletoBradesco();

        List<Boleto> boletos = new ArrayList<Boleto>();
        boletos.add(boletoBBNossoNumero10);
        boletos.add(boletoBBNossoNumero10);
        boletos.add(boletoBBNossoNumero10);

        //Informando o template personalizado:
        File templatePersonalizado = new File(ClassLoaders.getResource("/BoletoCarne3PorPagina.pdf").getFile());
        //Agrupando os boletos em apenas uma página
        File boletosPorPagina = groupInPages(boletos, "Carne3PorPagina.pdf", templatePersonalizado);
        try {
            ReportService.genReport(Files.fileToBytes(boletosPorPagina));
        } catch (IOException ex) {
            Logger.getLogger(IptuService.class.getName()).log(Level.SEVERE, null, ex);
        }
          
    }

    private static File groupInPages(List<Boleto> boletos, String filePath, File templatePersonalizado) {

        File arq = null;
        BoletoViewer boletoViewer = new BoletoViewer(boletos.get(0));
        boletoViewer.setTemplate(templatePersonalizado);

        List<byte[]> boletosEmBytes = new ArrayList<byte[]>(boletos.size());

        //Adicionando os PDF, em forma de array de bytes, na lista.
        for (Boleto bop : boletos) {
            boletosEmBytes.add(boletoViewer.setBoleto(bop).getPdfAsByteArray());
        }

        try {
            //Criando o arquivo com os boletos da lista
            arq = Files.bytesToFile(filePath, mergeFilesInPages(boletosEmBytes));
        } catch (Exception e) {
            throw new IllegalStateException("Erro durante geração do PDF! Causado por " + e.getLocalizedMessage(), e);
        }

        return arq;
    }

    private static Boleto crieBoletoBBNossoNumero10() {
        Titulo titulo = Exemplos.crieTitulo();

        ContaBancaria contaBancaria = titulo.getContaBancaria();
        contaBancaria.setBanco(BancosSuportados.BANCO_DO_BRASIL.create());
        contaBancaria.setNumeroDaConta(new NumeroDaConta(1234567));
        contaBancaria.setCarteira(new Carteira(12));

        titulo.setNossoNumero("1234567890");
        Boleto boletoBBNossoNumero10 = Exemplos.crieBoleto(titulo);

        return boletoBBNossoNumero10;
    }

    private static Boleto crieBoletoBradesco() {
        Titulo titulo = Exemplos.crieTitulo();

        ContaBancaria contaBancaria = titulo.getContaBancaria();
        contaBancaria.setBanco(BancosSuportados.BANCO_BRADESCO.create());
        contaBancaria.setAgencia(new org.jrimum.domkee.financeiro.banco.febraban.Agencia(Integer.SIZE));
        contaBancaria.setCarteira(new Carteira(12));
        titulo.setNossoNumero("01234567891");
        contaBancaria.setNumeroDaConta(new NumeroDaConta(1234567));

        Boleto boletoBradesco = Exemplos.crieBoleto(titulo);

        return boletoBradesco;
    }

    public static byte[] mergeFilesInPages(List<byte[]> pdfFilesAsByteArray) throws DocumentException, IOException {

        Document document = new Document();
        ByteArrayOutputStream byteOS = new ByteArrayOutputStream();

        PdfWriter writer = PdfWriter.getInstance(document, byteOS);

        document.open();

        PdfContentByte cb = writer.getDirectContent();
        float positionAnterior = 0;

        //Para cada arquivo da lista, cria-se um PdfReader, responsável por ler o arquivo PDF e recuperar informações dele.
        for (byte[] pdfFile : pdfFilesAsByteArray) {

            PdfReader reader = new PdfReader(pdfFile);

            //Faz o processo de mesclagem por página do arquivo, começando pela de número 1.
            for (int i = 1; i <= reader.getNumberOfPages(); i++) {

                float documentHeight = cb.getPdfDocument().getPageSize().getHeight();

                //Importa a página do PDF de origem
                PdfImportedPage page = writer.getImportedPage(reader, i);
                float pagePosition = positionAnterior;

                /*
                 * Se a altura restante no documento de destino form menor que a
                 * altura do documento, cria-se uma nova página no documento de
                 * destino.
                 */
                if ((documentHeight - positionAnterior) <= page.getHeight()) {
                    document.newPage();
                    pagePosition = 0;
                    positionAnterior = 0;
                }

                //Adiciona a página ao PDF destino
                cb.addTemplate(page, 0, pagePosition);

                positionAnterior += page.getHeight();
            }
        }

        byteOS.flush();
        document.close();

        byte[] arquivoEmBytes = byteOS.toByteArray();
        byteOS.close();

        return arquivoEmBytes;
    }
}
